#pragma once 
#include "Util.hpp"
#include "db.hpp"
#include "online.hpp" 
#include "room.hpp"
#include "session.hpp"
#include "matcher.hpp" 

#define WEB_ROOT "./wwwroot/"

class gobang_server
{
private:
  wsserver_t _wssrv;  
  user_table _ut;
  online_manager _om;
  room_manage _rm; 
  session_manage _sm; 
  mather _match; 
  std::string _web_root; 

private:

  //静态资源请求
  void file_handler(wsserver_t::connection_ptr& con)
  {
    websocketpp::http::parser::request req = con->get_request(); //获取请求
    std::string uri = req.get_uri(); 
    std::string pathname = _web_root + uri;
    if(pathname.back() == '/')
    {
      //是一个目录
      pathname += "index.html";
    }
    std::string body; 
    size_t size = 0 ;
    bool ret = UtilFile::read(pathname,body,size); 
    if(ret  == false)
    {
      //文件不存在
      body = "<html><head><meta charset=\"UTF-8\"/></head><body><h1>404 NOT FOUND </h1></body></html>";
      con->set_status(websocketpp::http::status_code::value::not_found);
      con->set_body(body);
      return ;
    }
    // 文件存在
    con->set_status(websocketpp::http::status_code::value::ok);
    con->append_header("Content-Length",std::to_string(size)); 
    con->set_body(body);
    DLOG("发送资源 ： %s, 大小：%ld",pathname.c_str(),size);
  }

  void http_resp(wsserver_t::connection_ptr con,bool result , websocketpp::http::status_code::value code , const std::string& reason)
  {
    Json::Value resp; 
    std::string body; 
    resp["result"] = result; 
    resp["reason"] = reason;
    con->set_status(code); 
    
    //反序列化 
    UtilJson::serialize(resp,body); 
    con->set_body(body);
    con->append_header("Content-Type","application/json");
  }
  //用户注册功能请求
  void user_register(wsserver_t::connection_ptr con)
  {
    //获取请求
    websocketpp::http::parser::request req = con->get_request(); 
    //获取正文 
    std::string req_body = con->get_request_body(); 

    Json::Value reg_info;
    bool ret = UtilJson::unserialize(req_body,reg_info); //反序列化 
    if(ret == false)
    {
      //反序列化失败 
      ELOG("反序列化失败");
      return http_resp(con,false,websocketpp::http::status_code::value::bad_request,"请求格式错误！"); 
    }
    
    if(reg_info["username"].isNull() || reg_info["password"].isNull()) 
    {
      //用户名和密码有一个为空，无法注册  
      ELOG("用户名或密码为空");
      return http_resp(con,false,websocketpp::http::status_code::value::bad_request,"用户或者密码不能为空"); 
    }

    if(reg_info["username"].asString().size() < 6 || reg_info["password"].asString().size() < 6)
    {
      return http_resp(con,false,websocketpp::http::status_code::value::bad_request,"用户名或密码长度不能小于8位"); 
    }
    
    //执行sql插入
    ret = _ut.insert(reg_info);
    if(ret == false)
    {
      //插入失败
      ELOG("数据库插入失败");
      return http_resp(con,false,websocketpp::http::status_code::value::bad_request,"该用户名已被占用！"); 
    } 
      DLOG("注册成功！");
      return http_resp(con,true,websocketpp::http::status_code::value::ok,"注册成功！"); 
  }
  //用户登陆功能请求
  void user_login(wsserver_t::connection_ptr con)
  {
    //1.对登陆信息进行序列化
    Json::Value login_info; 
    websocketpp::http::parser::request req = con->get_request(); 
    std::string req_body = con->get_request_body(); 
    bool ret = UtilJson::unserialize(req_body,login_info);
    if(ret == false)
    {
       //反序列化失败 
      ELOG("反序列化失败");
      return http_resp(con,false,websocketpp::http::status_code::value::bad_request,"请求格式错误！"); 
    }
    //2.校验信息是否正确 
    if(login_info["username"].isNull() || login_info["password"].isNull())
    {
      ELOG("用户名或者密码不能为空");
      return http_resp(con,false,websocketpp::http::status_code::value::bad_request,"用户名或密码不能为空！"); 
    }

    ret = _ut.login(login_info); 
    if(ret == false)
    {
      ELOG("用户名或密码错误");
      return http_resp(con,false,websocketpp::http::status_code::value::bad_request,"用户名或密码错误"); 
    }
    //验证成功，创建session 
    session_ptr ssp = _sm.create_session(login_info["id"].asInt(),LOGIN);
    if(ssp.get() == nullptr)
    {
      DLOG("创建Session失败"); 
      return http_resp(con,false,websocketpp::http::status_code::value::internal_server_error,"Session创建失败"); 
    }
    _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT);
    //设置响应头部 
    std::string cookie_ssid = "SSID=" + std::to_string(ssp->ssid()); 
    con->append_header("Set-Cookie",cookie_ssid); 
    http_resp(con,true,websocketpp::http::status_code::value::ok,"登陆成功");
  }

  bool get_cookie_val(const std::string& cookie,const std::string& key ,std::string& value){
    std::string sep = "; "; 
    std::vector<std::string> cookie_arr;
    UtilString::split(cookie,sep,cookie_arr);
    for(auto& s : cookie_arr)
    {
      std::vector<std::string> tmp_arr; 
      UtilString::split(s,"=",tmp_arr); 
      if(tmp_arr.size() != 2) continue; 
      if(tmp_arr[0] == key)
      {
        value = tmp_arr[1]; 
        return true;
      }
    }
    return false;
  }
  //获取用户信息请求
  void user_info(wsserver_t::connection_ptr con)
  {
    //1.获取cookie 
    std::string cookie_str = con->get_request_header("Cookie"); 
    if(cookie_str.empty())
    {
      //cookie获取失败  
      return http_resp(con,true,websocketpp::http::status_code::value::bad_request,"cookie获取失败"); 
    }
    //取出SSID 
    std::string ssid_str; 
    bool ret = get_cookie_val(cookie_str,"SSID",ssid_str);
    if(ret == false)
    {
      //ssid获取失败 
      return http_resp(con,true,websocketpp::http::status_code::value::bad_request,"找不到ssid信息"); 
    }

    //在Session管理中查找对应的会话信息 
    session_ptr ssp = _sm.get_session_by_ssid(std::stol(ssid_str)); 
    if(ssp.get() == nullptr)
    {
      //没有找到session
      return http_resp(con,true,websocketpp::http::status_code::value::bad_request,"登陆过期，请重新登陆"); 
    }

    //从数据库中取出用户信息 
    int uid = ssp->get_user(); //通过session获取用户id 
    Json::Value user; 
    ret = _ut.select_by_id(uid,user);
    if(ret == false)
    {
      //用户数据获取失败
      return http_resp(con,true,websocketpp::http::status_code::value::bad_request,"数据库获取数据失败"); 
    }

    std::string body; 
    UtilJson::serialize(user,body);
    con->set_body(body);
    con->set_status(websocketpp::http::status_code::value::ok); 
    con->append_header("Content-Type","application/json");
    _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT);//刷新session信息
  }


  //http请求处理函数
  void http_callback(websocketpp::connection_hdl hd1){

    //获取连接
    wsserver_t::connection_ptr con = _wssrv.get_con_from_hdl(hd1);  
    //获取http请求 
    websocketpp::http::parser::request req = con->get_request(); 
    std::string method = req.get_method(); 
    std::string uri = req.get_uri();
    if("POST" == method && "/reg" == uri )
    {
      //用户注册请求
      user_register(con);
    }else if("POST" == method && "/login" == uri)
    {
      //用户登陆请求
      user_login(con);
    }else if("GET" == method && "/user_info" == uri)
    {
      //用户信息获取请求
      user_info(con);
    }else 
    {
      //静态资源请求 
      file_handler(con); 
    }
  }
  
  
  void ws_resp(wsserver_t::connection_ptr con , Json::Value& resp)
  {
    //序列化 
    std::string body; 
    UtilJson::serialize(resp,body); 
    con->send(body); 
  }
  
  session_ptr get_session_by_cookie(wsserver_t::connection_ptr con)
  {
    //1.登陆验证
    Json::Value err_resp;
    err_resp["optype"] = "hall_ready";
    std::string cookie_str = con->get_request_header("Cookie"); 
    if(cookie_str.empty())
    {
      DLOG("没有找到Cookie信息，请重新登陆");
      err_resp["reason"] = "没有找到Cookie信息，请重新登陆"; 
      err_resp["result"] = false; 
      ws_resp(con,err_resp);
      return session_ptr();
    }
    //从cookie中取出ssid 
    std::string ssid_str;
    bool ret = get_cookie_val(cookie_str,"SSID",ssid_str); 
    if(ret == false)
    {
      //ssid获取失败
      err_resp["reason"] = "ssid信息获取失败，请重新登陆"; 
      err_resp["result"] = false; 
      DLOG("ssid信息获取失败，请重新登陆");

      ws_resp(con,err_resp);
      return session_ptr();
    }
    //获取session信息 
    session_ptr ssp = _sm.get_session_by_ssid(std::stol(ssid_str));
    if(ssp.get() == nullptr)
    {
      //session获取失败
      err_resp["reason"] = "session获取失败，请重新登陆"; 
        DLOG("session获取失败，请重新登陆 %s",ssid_str.c_str());
      err_resp["result"] = false; 
      ws_resp(con,err_resp);
      return session_ptr();
    }
    return ssp; 
  }

  void wsopen_game_hall(wsserver_t::connection_ptr con)
  {
    Json::Value err_resp;
    session_ptr ssp = get_session_by_cookie(con);
    if(ssp.get() == nullptr)
    {
      //获取失败
      DLOG("ssp获取失败");
      return;
    }
    //判断是否重复登陆 
    int uid = ssp->get_user();
    if(_om.is_in_game_hall(uid) || _om.is_in_game_room(uid))
    {
      //用户重复登陆 
      err_resp["reason"] = "玩家重复登陆"; 
      err_resp["result"] = false; 
      return ws_resp(con,err_resp); 
    }
    //将客户端连接进游戏大厅 
    _om.enter_game_hall(uid,con);

    //给客户端响应连接建立成功
    Json::Value resp;
    resp["result"] = true; 
    resp["optype"] = "hall_ready";
    std::string body; 
    UtilJson::serialize(resp,body); 
    con->send(body); 
    //设置session永久存在 
    _sm.set_session_expire_time(ssp->ssid(),SESSION_FOREVER);
  }

  void wsopen_game_room(wsserver_t::connection_ptr con)
  {
    session_ptr ssp = get_session_by_cookie(con); 
    if(ssp.get() == nullptr )
    {
      return ;
    }
    
    int uid = ssp->get_user();
    Json::Value resp; 
    //2.当前用户是否在在线用户管理的游戏房间或者游戏大厅 
    if(_om.is_in_game_hall(uid) || _om.is_in_game_room(uid))
    {
      resp["result"] = false ; 
      resp["reason"] = "玩家重复登陆";
      resp["optype"] = "room_ready";
      return ws_resp(con,resp); 
    }
    //判断当前用户房间是否创建好了 
    room_ptr rp = _rm.get_room_by_uid(uid);
    if(rp.get() == nullptr)
    {
      resp["result"] = false ;
      resp["reason"] = "没有找到房间信息";
      resp["optype"] = "room_ready"; 
      return ws_resp(con,resp); 
    }
    //将当前用户添加进在线用户管理的游戏房间中 
    _om.enter_game_room(uid,con); 
    //设置session为永久
    _sm.set_session_expire_time(ssp->ssid(),SESSION_FOREVER);

    //回复房间准备完毕
    resp["optype"] = "room_ready";
    resp["room_id"] = rp->id();
    resp["uid"] = uid; 
    resp["white_id"] = rp->get_white_uid();
    resp["black_id"] = rp->get_black_uid();
    resp["result"] = true;
    ws_resp(con,resp);
  }

  //打开连接处理函数
  void wsopen_callback(websocketpp::connection_hdl hd1){ 
    DLOG("打开了一个新连接");
    //获取连接
    wsserver_t::connection_ptr con = _wssrv.get_con_from_hdl(hd1);  
    //获取http请求 
    websocketpp::http::parser::request req = con->get_request(); 
    std::string uri = req.get_uri(); 
    if("/hall" == uri)
    {
      wsopen_game_hall(con);
    }else if("/room" == uri)
    {
      wsopen_game_room(con);
    } 
  }

  void wsclose_game_hall(wsserver_t::connection_ptr con)
  {
    session_ptr ssp = get_session_by_cookie(con);
    if(ssp.get() == nullptr)
    {
      return;
    }
    //玩家退出游戏大厅
    _om.exit_game_hall(ssp->get_user());
    //恢复session的生命周期 
    _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT);
  }

  void wsclose_game_room(wsserver_t::connection_ptr con)
  {
    DLOG("进入wsclose_game_room函数");
    session_ptr ssp = get_session_by_cookie(con);
    if(ssp.get() == nullptr)
    {
      
    DLOG("ssp为空指针");
      return; 
    }
    //1.把用户从在线管理中移除
    _om.exit_game_room(ssp->get_user()); 
    //2.把session设置定时
    _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT); 
    //3.把用户从房间移除
    _rm.remove_room_user(ssp->get_user());
  }

  //关闭连接处理函数
  void wsclose_callback(websocketpp::connection_hdl hd1){
    //获取连接
    DLOG("进入关闭连接处理函数");
    wsserver_t::connection_ptr con = _wssrv.get_con_from_hdl(hd1);  
    //获取http请求 
    websocketpp::http::parser::request req = con->get_request(); 
    std::string uri = req.get_uri(); 
    if("/hall" == uri)
    {
      wsclose_game_hall(con);
    }else if("/room" == uri)
    {
      wsclose_game_room(con);
    } 
  }
  
  void wsmsg_game_hall(wsserver_t::connection_ptr con,wsserver_t::message_ptr& msg)
  {
    Json::Value resp_json; 
    std::string resp_body; 

    //1.身份验证
    session_ptr ssp = get_session_by_cookie(con); 
    if(ssp.get() == nullptr)
    {
      return; 
    }

    //获取请求信息 
    std::string req_body = msg->get_payload(); 
    Json::Value req;
    bool ret = UtilJson::unserialize(req_body,req); //反序列化
    if(ret == false)
    {
      resp_json["result"] = false ; 
      resp_json["reason"] = "请求信息解析失败"; 
      return ws_resp(con,resp_json);
    }
    
    //对于请求进行处理 
    if(!req["optype"].isNull() && req["optype"].asString() == "match_start") 
    {
      //开始匹配对战请求
      _match.add(ssp->get_user()); //把用户加入匹配队列 
      resp_json["result"] = true; 
      resp_json["optype"] = "match_start";
      return ws_resp(con,resp_json);
    }
    else if(!req["optype"].isNull() && req["optype"].asString() == "match_stop")
    {
      //停止匹配请求
      _match.del(ssp->get_user()); //把用户加入匹配队列 
      resp_json["result"] = true; 
      resp_json["optype"] = "match_stop";
      return ws_resp(con,resp_json);
    }

    //未知请求
    resp_json["result"] = false ; 
    resp_json["optype"] = "unkonw";
    ws_resp(con,resp_json);
  }

  void wsmsg_game_room(wsserver_t::connection_ptr con,wsserver_t::message_ptr& msg)
  {
    DLOG("进入wsmsg_game_room函数");
    Json::Value resp; 
    session_ptr ssp = get_session_by_cookie(con); 
    if(ssp.get() == nullptr)
    {
      DLOG("ssp=null");

      return ;
    }
    room_ptr rp = _rm.get_room_by_uid(ssp->get_user());
    //房间为空
    if(rp.get() == nullptr)
    {
      resp["optype"] = "unkonw"; 
      resp["result"] = false; 
      resp["reason"] = "没有找到房间信息";
      DLOG("没有找到玩家信息");
      return ws_resp(con,resp);
    }
    //对消息反序列化 
    Json::Value req; 
    std::string req_body = msg->get_payload();
    DLOG("房间请求信息 %s",req_body.c_str());
    bool ret = UtilJson::unserialize(req_body,req);
    if(ret == false)
    {
      DLOG("ret = false");
      resp["optype"] = "unkonw"; 
      resp["result"] = false; 
      resp["reason"] = "请求解析失败"; 
      return ws_resp(con,resp);
    }
    //通过房间信息处理请求
    DLOG("房间-收到请求开始处理");
    rp->handler_request(req);
  }
  
    //消息处理函数
  void wsmsg_callback(websocketpp::connection_hdl hd1,wsserver_t::message_ptr msg){
    //获取连接
    wsserver_t::connection_ptr con = _wssrv.get_con_from_hdl(hd1);  
    //获取http请求 
    websocketpp::http::parser::request req = con->get_request(); 
    std::string uri = req.get_uri(); 
    if("/hall" == uri)
    {
      wsmsg_game_hall(con,msg);
    }else if("/room" == uri)
    {
      wsmsg_game_room(con,msg);
    }
  }

public:
  gobang_server(const std::string& host,
                const std::string& user, 
                const std::string& password,
                const std::string& dbname,
                const uint16_t port = 3306,
                const std::string& wwwroot = WEB_ROOT) :
                _ut(host,user,password,dbname,port),
                _rm(&_ut,&_om), 
                _sm(&_wssrv),
                _match(&_ut,&_rm,&_om)
                ,_web_root(wwwroot)
  {
    _wssrv.set_access_channels(websocketpp::log::alevel::none) ;//设置日志等级  
    _wssrv.init_asio(); //初始化asio调度器
    _wssrv.set_reuse_addr(true); //启用地址复用 
    _wssrv.set_http_handler(std::bind(&gobang_server::http_callback,this,std::placeholders::_1));//绑定http请求处理函数
    _wssrv.set_open_handler(std::bind(&gobang_server::wsopen_callback,this,std::placeholders::_1)); //绑定open处理函数 
    _wssrv.set_close_handler(std::bind(&gobang_server::wsclose_callback,this,std::placeholders::_1)); //绑定close处理函数 
    _wssrv.set_message_handler(std::bind(&gobang_server::wsmsg_callback,this,std::placeholders::_1,std::placeholders::_2));//绑定消息处理函数
    
  }

  void start(uint16_t port)
  {
    _wssrv.listen(port); 
    _wssrv.start_accept(); 
    _wssrv.run();
  }
  ~gobang_server(){} 


};
